﻿using System;
using System.Collections.Generic;
using System.Text;

namespace PCR.Logic
{
    using PCR.Base;
    
    /// <summary>
    /// Patient report xml database
    /// </summary>
    internal class XmlPatientReport : IObjectAccess<IPatientReport>
    {
        /// <summary>
        /// Xml tag definition
        /// </summary>
        static readonly string PatientReports_Tag = "PatientReports";
        static readonly string PatientReport_Tag = "PatientReport";
        static readonly string ObjectID_Tag = "ObjectID";
        static readonly string ReportDate_Tag = "ReportDate";
        static readonly string ReportID_Tag = "ReportID";
        static readonly string Name_Tag = "Name";
        static readonly string Age_Tag = "Age";
        static readonly string Gender_Tag = "Gender";
        static readonly string CheckDate_Tag = "CheckDate";
        static readonly string SampleID_Tag = "SampleID";
        static readonly string SampleReceiveDate_Tag = "SampleReceiveDate";
        static readonly string CheckMethod_Tag = "CheckMethod";
        static readonly string SampleType_Tag = "SampleType";
        static readonly string SampleState_Tag = "SampleState";
        static readonly string PatientVirus_Tag = "PatientVirus";
        static readonly string PatientVirusID_Tag = "PatientVirusID";
        static readonly string PatientVirusCT_Tag = "PatientVirusCT";
        static readonly string PatientReferCellCT_Tag = "PatientReferCellCT";
        static readonly string CellPrinciple_Tag = "CellPrinciple";
        static readonly string PathologyPrinciple_Tag = "PathologyPrinciple";

        protected System.Xml.XmlDocument _doc;
        protected string _filename;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="doc">xml doccument</param>
        /// <param name="filename">xml filename, used to store data to the file</param>
        public XmlPatientReport(System.Xml.XmlDocument doc,
            string filename)
        {
            System.Diagnostics.Debug.Assert(doc != null);
            System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(filename));

            this._doc = doc;
            this._filename = filename;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="doc">xml doccument</pparam>
        /// <param name="filename">xml filename, used to store data to the file</param>
        public void Append(IPatientReport item)
        {
            System.Diagnostics.Debug.Assert(item != null);

            // First get the <PatientReports> node
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            AppendInternal(item, nodePatientReports);
            Save();
        }

        public void Append(IList<IPatientReport> items)
        {
            System.Diagnostics.Debug.Assert(items != null);
            if (items.Count == 0)
                return;

            // First get the <PatientReports> node
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            foreach (IPatientReport item in items)
            {
                AppendInternal(item, nodePatientReports);
            }

            Save();
        }

        public void Update(IPatientReport item)
        {
            System.Diagnostics.Debug.Assert(item != null);
            UpdateByID(item.ObjectID, item);
        }

        public void UpdateByID(Guid id, IPatientReport item)
        {
            // Find the specified node
            System.Xml.XmlNode patientNode = FindNodeByID(item.ObjectID);
            System.Diagnostics.Debug.Assert(patientNode != null);
            patientNode.RemoveAll();
            FillNodeWithPatientReport(item, patientNode);
            Save();
        }

        public void Delete(IPatientReport item)
        {
            System.Diagnostics.Debug.Assert(item != null);
            DeleteByID(item.ObjectID);
        }

        public void Delete(IList<IPatientReport> items)
        {

        }

        public void DeleteByID(Guid id)
        {
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            // Find the specified node
            System.Xml.XmlNode nodePatientReport = FindNodeByID(id);
            System.Diagnostics.Debug.Assert(nodePatientReport != null);

            nodePatientReports.RemoveChild(nodePatientReport);

            Save();
        }

        public void DeleteAll()
        {
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            nodePatientReports.RemoveAll();
            Save();
        }

        public IList<IPatientReport> SelectAll()
        {
            IList<IPatientReport> patientReports = new List<IPatientReport>();

            // First get the <PatientReports> node, 
            // and this may create a new node for it
            // it does not affect us to do the following operations
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            // Select all the <PatientReport> node list
            System.Xml.XmlNodeList nodePatientReportsList = nodePatientReports.SelectNodes(PatientReport_Tag);
            foreach (System.Xml.XmlNode nodePatientReport in nodePatientReportsList)
            {
                // Create a new instance
                IPatientReport item = CreatePatientReport();

                // Fill the instance with xml node
                if (!FillPatientReportWithNode(item, nodePatientReport))
                {
                    // Do some error information
                    Logger.Warn("Invalid PatientReport xml node, igore it!");
                    continue;
                }

                // Add to the PatientReports list
                patientReports.Add(item);
            }

            return patientReports;
        }

        public IPatientReport SelectByID(Guid id)
        {
            // Find the xml node by ID
            System.Xml.XmlNode nodePatientReport = FindNodeByID(id);
            if (nodePatientReport == null)
                return null;

            // Create a new instance
            IPatientReport item = CreatePatientReport();

            // Fill the instance with xml node
            if (!FillPatientReportWithNode(item, nodePatientReport))
            {
                // Do some error information
                Logger.Error("Invalid virus type xml node!");
                return null;
            }

            return item;
        }

        public IList<IPatientReport> Search(ISearchFilter<IPatientReport> filter)
        {
            return null;
        }

        protected void AppendInternal(IPatientReport item, System.Xml.XmlNode nodePatientReports)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            // Check if it is existed, if existed just report error
            if (IsExisted(item, nodePatientReports))
            {
                System.Diagnostics.Debug.Assert(false,
                    string.Format("Duplicated Object ID {0}, Please check it!", item.ObjectID.ToString()));

                throw new ApplicationException(string.Format(
                    "Duplicated Object ID {0}, Please check it!",
                    item.ObjectID.ToString()));
            }

            // Then append to the xml file
            // Create a new <PatientReport> node
            System.Xml.XmlElement elemPatientReport = this._doc.CreateElement(PatientReport_Tag);

            FillNodeWithPatientReport(item, elemPatientReport);

            nodePatientReports.AppendChild(elemPatientReport);
        }

        protected bool FillNodeWithPatientReport(IPatientReport item, System.Xml.XmlNode elemPatientReport)
        {
            // Append <ObjectID> attribute
            System.Xml.XmlAttribute attr = this._doc.CreateAttribute(ObjectID_Tag);
            attr.Value = item.ObjectID.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <ReportDate> attribute
            attr = this._doc.CreateAttribute(ReportDate_Tag);
            attr.Value = item.ReportDate.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <ReportID> attribute
            attr = this._doc.CreateAttribute(ReportID_Tag);
            attr.Value = item.ReportID.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <Name> attribute
            attr = this._doc.CreateAttribute(Name_Tag);
            attr.Value = item.Name.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <Age> attribute
            attr = this._doc.CreateAttribute(Age_Tag);
            attr.Value = item.Age.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <Gender> attribute
            attr = this._doc.CreateAttribute(Gender_Tag);
            attr.Value = item.Gender.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <CheckDate> attribute
            attr = this._doc.CreateAttribute(CheckDate_Tag);
            attr.Value = item.CheckDate.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <SampleID> attribute
            attr = this._doc.CreateAttribute(SampleID_Tag);
            attr.Value = item.SampleID.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <SampleReceiveDate> attribute
            attr = this._doc.CreateAttribute(SampleReceiveDate_Tag);
            attr.Value = item.SampleReceiveDate.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <CheckMethod> attribute
            attr = this._doc.CreateAttribute(CheckMethod_Tag);
            attr.Value = item.CheckMethod.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <SampleType> attribute
            attr = this._doc.CreateAttribute(SampleType_Tag);
            attr.Value = item.SampleType.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <SampleState> attribute
            attr = this._doc.CreateAttribute(SampleState_Tag);
            attr.Value = item.SampleState.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <CellPrinciple> attribute
            attr = this._doc.CreateAttribute(CellPrinciple_Tag);
            attr.Value = item.CellPrinciple.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Append <CellPrinciple> attribute
            attr = this._doc.CreateAttribute(PathologyPrinciple_Tag);
            attr.Value = item.PathologyPrinciple.ToString();
            elemPatientReport.Attributes.Append(attr);

            // Create PatientViruses node and attach it to <PatientReport> node
            //System.Xml.XmlElement elemPatientViruses = this._doc.CreateElement(PatientViruses_Tag);
            //elemPatientReport.AppendChild(elemPatientViruses);
            foreach (IPatientVirus patientVirus in item.PatientVirusList)
            {
                // Create <PatientVirus> list
                System.Xml.XmlElement elemPatientVirus = this._doc.CreateElement(PatientVirus_Tag);

                // Append <PatientVirusID> attribute
                attr = this._doc.CreateAttribute(PatientVirusID_Tag);
                attr.Value = patientVirus.VirusTypeID.ToString();
                elemPatientVirus.Attributes.Append(attr);

                // Append <PatientVirusCT> attribute
                attr = this._doc.CreateAttribute(PatientVirusCT_Tag);
                attr.Value = patientVirus.VirusCTVal.ToString();
                elemPatientVirus.Attributes.Append(attr);
                
                // Append <PatientReferCellCT> attribute
                attr = this._doc.CreateAttribute(PatientReferCellCT_Tag);
                attr.Value = patientVirus.ReferCellCTVal.ToString();
                elemPatientVirus.Attributes.Append(attr);
                
                // Append to <PatientViruses> element
                elemPatientReport.AppendChild(elemPatientVirus);
            }

            return true;
        }

        protected System.Xml.XmlNode GetPatientReportsNode()
        {
            System.Xml.XmlNode nodeRoot = this._doc.SelectSingleNode("PCRConfig");
            System.Diagnostics.Debug.Assert(nodeRoot != null, "PCRConfig xml tag can not be empty!");
            System.Xml.XmlNode nodePatientReports = nodeRoot.SelectSingleNode(PatientReports_Tag);
            if (nodePatientReports == null) // Add the node if not existed
            {
                nodePatientReports = this._doc.CreateElement(PatientReports_Tag);
                nodeRoot.AppendChild(nodePatientReports);
            }

            return nodePatientReports;
        }

        protected void Save()
        {
            this._doc.Save(this._filename);
        }

        protected bool IsExisted(IPatientReport item, System.Xml.XmlNode nodePatientReports)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            return false;
        }

        protected IPatientReport CreatePatientReport()
        {
            IPatientReport patientReport = ObjectFactory.CreateObject(ObjectType.PatientReport) as IPatientReport;
            System.Diagnostics.Debug.Assert(patientReport != null);
            return patientReport;
        }

        protected IPatientVirus CreateVirusType()
        {
            IPatientVirus patientVirus = ObjectFactory.CreateObject(ObjectType.PatientVirus) as IPatientVirus;
            System.Diagnostics.Debug.Assert(patientVirus != null);
            return patientVirus;
        }

        protected bool FillPatientReportWithNode(IPatientReport item, System.Xml.XmlNode node)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(node != null);

            System.Xml.XmlAttribute attr;

            // Get <ObjectID> attribute
            attr = node.Attributes[ObjectID_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.ObjectID = new Guid(attr.Value);

            // Get <ReportDate> attribute
            attr = node.Attributes[ReportDate_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.ReportDate = DateTime.Parse(attr.Value);

            // Get <ReportID> attribute
            attr = node.Attributes[ReportID_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.ReportID = attr.Value;

            // Get <Name> attribute
            attr = node.Attributes[Name_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.Name = attr.Value;

            // Get <Age> attribute
            attr = node.Attributes[Age_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.Age = UInt32.Parse(attr.Value);

            // Get <Gender> attribute
            attr = node.Attributes[Gender_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.Gender = (Gender)Enum.Parse(typeof(Gender), attr.Value);

            // Get <CheckDate> attribute
            attr = node.Attributes[CheckDate_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.CheckDate = DateTime.Parse(attr.Value);

            // Get <SampleID> attribute
            attr = node.Attributes[SampleID_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.SampleID = attr.Value;

            // Get <SampleReceiveDate> attribute
            attr = node.Attributes[SampleReceiveDate_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.SampleReceiveDate = DateTime.Parse(attr.Value);

            // Get <CheckMethod> attribute
            attr = node.Attributes[CheckMethod_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.CheckMethod = (CheckMethod)Enum.Parse(typeof(CheckMethod), attr.Value);

            // Get <SampleType> attribute
            attr = node.Attributes[SampleType_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.SampleType = (SampleType)Enum.Parse(typeof(SampleType), attr.Value);

            // Get <SampleState> attribute
            attr = node.Attributes[SampleState_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.SampleState = (SampleState)Enum.Parse(typeof(SampleState), attr.Value);

            // Get <CellPrinciple> attribute
            attr = node.Attributes[CellPrinciple_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.CellPrinciple = attr.Value;

            // Get <PathologyPrinciple> attribute
            attr = node.Attributes[PathologyPrinciple_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.PathologyPrinciple = attr.Value;

            // Find all the <PatientVirus> nodes
            System.Xml.XmlNodeList nodePatientVirusList = node.SelectNodes(PatientVirus_Tag);
            foreach (System.Xml.XmlNode nodePatientVirus in nodePatientVirusList)
            {
                IPatientVirus patientVirus = CreateVirusType();

                // Get <PatientVirusID> attribute
                attr = nodePatientVirus.Attributes[PatientVirusID_Tag];
                System.Diagnostics.Debug.Assert(attr != null);
                patientVirus.VirusTypeID = new Guid(attr.Value);

                // Get <PatientVirusCT> attribute
                attr = nodePatientVirus.Attributes[PatientVirusCT_Tag];
                System.Diagnostics.Debug.Assert(attr != null);
                patientVirus.VirusCTVal = double.Parse(attr.Value);
                
                // Get <PatientReferCellCT> attribute
                attr = nodePatientVirus.Attributes[PatientReferCellCT_Tag];
                System.Diagnostics.Debug.Assert(attr != null);
                patientVirus.ReferCellCTVal = double.Parse(attr.Value);
                
                // Add to list
                item.PatientVirusList.Add(patientVirus);
            }

            return true;
        }

        protected System.Xml.XmlNode FindNodeByID(Guid id)
        {
            System.Xml.XmlNode nodePatientReports = GetPatientReportsNode();
            System.Diagnostics.Debug.Assert(nodePatientReports != null);

            // Select all the <PatientReport> node list
            System.Xml.XmlNodeList nodePatientReportsList = nodePatientReports.SelectNodes(PatientReport_Tag);
            foreach (System.Xml.XmlNode nodePatientReport in nodePatientReportsList)
            {
                // Judge the ID
                System.Xml.XmlAttribute objAttr = nodePatientReport.Attributes[ObjectID_Tag];
                System.Diagnostics.Debug.Assert(objAttr != null);

                if (objAttr.Value == id.ToString())
                {
                    return nodePatientReport;
                }
            }

            return null;
        }

        protected ILogger Logger
        {
            get
            {
                return PCRRoot.Instance.LogManager.GetLogger(typeof(XmlPatientReport));
            }
        }
    }
    
}
